﻿using System;

/*
 * 创建人：
 * 
 * 时间：
 * 
 * 说明：
 * 
 * 
 * 修改说明：
 * 
 * 
 */


namespace ConsoleTest.ChapterFive
{
    /// <summary>
    /// 异步方法调用等
    /// </summary>
    public  class Asynchronous
    {
       
        /*基础知识
         * 一个线程调用另外一个方法后直到方法完成才能继续执行，这种执行方式是同步的
         * 这种执行方式被认为是比较耗费资源的，因为线程在等待方法完成的时间内是阻塞的。
         * 
         */

        /*基础知识
         * 定义委托之后，编译器自动提供两个名为BeginInvoke和EndInvoke的方法，该方法的签名与委托的签名
         * 相同，另外参数列表后面增加了两个参数，一个用于指定当委托方法完成后需要执行的回调方法
         * 另外一个指定一个对象，该对象可以被回调方法和触发异步调用的线程使用。
         * 另外BeginInvoke方法的返回值IAsyncResult 包含对该对象的引用。
         * 
         * 异步调用的结果由EndInvoke方法获得，由编译器负责确保EndInvoke方法返回值与委托类型返回值相同
         * 对EndInvoke方法的调用是阻塞式的，意味着只有异步执行有效结束后才能够返回
         * 
         */

        /* IAsyncResult表示一步操作的状态
        [ComVisible(true)]
        public interface IAsyncResult
        {
        // Properties
        object AsyncState { get; }  --包含了对BeginInvoke方法最后一个状态对象的引用
        WaitHandle AsyncWaitHandle { get; }
        bool CompletedSynchronously { get; }
        bool IsCompleted { get; }
        } 
         */

        /*对异步委托操作结果的封装
         * Encapsulates the results of an asynchronous operation on an asynchronous delegate.
         * 异步调用返回的结果为IAsyncResult接口，System.Runtime.Remoting.Messaging.AsyncResult类实现了该接口
         * AsyncResult 类与使用委托所进行的异步方法调用一起使用。从该委托的 BeginInvoke 方法返回的 IAsyncResult 可以强制转换为 AsyncResult。
         * AsyncResult 具有 AsyncDelegate 属性，该属性保存对其调用异步调用的委托对象。
         */

        /// <summary>
        /// 异步调用
        /// </summary>
        public static void BeginInvoke()
        {
            int a = 0;
            SumDelegate delg = new SumDelegate(Sum);
            System.IAsyncResult async = delg.BeginInvoke(10, 10, null, null);
            a = delg.EndInvoke(async);
            System.Console.WriteLine("计算结果："+a);
        }

        //(注意：)委托不能被声明为static 
        //如果一个类不需要实例化，那么可以声明为静态类。声明为静态类的类型默认是sealed ，即不能继承的

        /// <summary>
        /// 委托
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public   delegate int SumDelegate(int a, int b);
        
        /// <summary>
        /// 与委托签名相同的方法
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private static int Sum(int a, int b)
        {
            System.Console.WriteLine("Thread:{0} Sum={1}!"
                , System.Threading.Thread.CurrentThread.ManagedThreadId
                ,a+b);
            return a + b;
        }


        //初始值false表示AutoResetEvent为终止状态
        private static System.Threading.AutoResetEvent ev = new System.Threading.AutoResetEvent(false);
        /// <summary>
        /// 使用回调方法的异步调用
        /// </summary>
        public static void InvokeCallBack()
        {
            //一个与委托签名相同的方法可以直接赋值给委托
            //编译器生成的代码：new SumDelegate(Asynchronous.Sum).BeginInvoke(10, 10, new AsyncCallback(Asynchronous.SumDone), null);
            //可见方法可以直接赋值给委托只是一个语法上面的技巧，其本质上仍然是使用了委托的构造函数
            SumDelegate del = Sum;

            del.BeginInvoke(10, 10, SumDone, null);
            //显示启动异步调用的线程
            System.Console.WriteLine("Thread:{0} begin invoke called! Wait for Sum Done!",System.Threading.Thread.CurrentThread.ManagedThreadId);
            //使用事件实现同步
            ev.WaitOne();
            System.Console.WriteLine("Sum Done, Bye....................");
        }

        /// <summary>
        /// 回调方法
        /// </summary>
        private static void SumDone(System.IAsyncResult async)
        {
                //方法内部将接口强制转换为AysncResult对象
            System.Threading.Thread.Sleep(1000);
            SumDelegate del = ((System.Runtime.Remoting.Messaging.AsyncResult)async).AsyncDelegate as SumDelegate;
            int result = del.EndInvoke(async);
            //（注意：）调用回调方法的线程与调用异步方法的线程是同一个线程
            System.Console.WriteLine("Thread:{0} CallBack methord Called!", System.Threading.Thread.CurrentThread.ManagedThreadId);

            //在回调方法中设置事件，并发送信号
            //将事件设置为终止状态，其他一个或者多个线程可以继续
            ev.Set();
        }

        /// <summary>
        /// 向回调方法传递状态
        /// </summary>
        public static void SendStateToCallBack()
        {
            //一个与委托签名相同的方法可以直接赋值给委托
            SumDelegate del = Sum;

            //注意：此时使用的是局部事件来实现同步
            System.Threading.AutoResetEvent localEv = new System.Threading.AutoResetEvent(false);

            del.BeginInvoke(10, 10, SumDoneUseState, localEv);
            //显示启动异步调用的线程
            System.Console.WriteLine("Thread:{0} begin invoke called! Wait for Sum Done!", System.Threading.Thread.CurrentThread.ManagedThreadId);
            //使用事件实现同步，该事件作为BeginInvoke方法的最后一个状态参数，可以在回调方法中使用
            localEv.WaitOne();
            System.Console.WriteLine("Sum Done, Bye....................");
        }

        /// <summary>
        /// 回调方法
        /// </summary>
        private static void SumDoneUseState(System.IAsyncResult async)
        {
            //方法内部将接口强制转换为AysncResult对象
            System.Threading.Thread.Sleep(1000);
            SumDelegate del = ((System.Runtime.Remoting.Messaging.AsyncResult)async).AsyncDelegate as SumDelegate;
            int result = del.EndInvoke(async);
            //（注意：）调用回调方法的线程与执行异步方法的线程是同一个线程
            System.Console.WriteLine("Thread:{0} CallBack methord Called!", System.Threading.Thread.CurrentThread.ManagedThreadId);

            //在回调方法中设置事件，并发送信号
            //将事件设置为终止状态，其他一个或者多个线程可以继续
            //async 的AsyncState引用了最后一个状态参数
            ((System.Threading.AutoResetEvent)async.AsyncState).Set();
        }
    }
}